﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Savannah.Core
{
    /// <summary>
    /// Az állatok ősosztálya.
    /// Tartalmazza MINDEN állat KöZöS tulajdonságait és viselkedésformáit.
    /// </summary>
    public abstract class AnimalBase
    {
        private Sex sex;                /*Az egyed neme (ENUM! - enums.cs osztályból)   [male/female]*/
        private AnimalType animalType;  /*Az egyed tipusa (ENUM! - enums.cs osztályból) [terrestial/aquatic]*/
        private int current_age;        /*Az egyed Jelenlegi életkora*/
        private int maximum_age;        /*Megengedett max életkor - ha ezt eléri, az egyed öregedés miatt kihal*/

        private int hungerLevel;        /*Tárolja az egyed éhségszintjét egy 10-es skálán*/
        private int thirstyLevel;       /*Tárolja az egyed szomjúságszintjét egy 10-es skálán*/

        private readonly int rangeOfView;       /*Látómező nagysága - ekkora területet lát be az egyed*/
        private readonly int rangeOfMove;       /*Ennyi egységet képes mozogni 1 napon*/

        private int survival;           //túlélési esély támadás esetén
        private int successorNumber;    //utódok száma

        /// <summary>
        /// állat elhelyezkedése a szavannán(x kord.)
        /// </summary>
        private int XCoordinate;
        /// <summary>
        /// állat elhelyezkedése a szavannán(y kord.)
        /// </summary>       
        private int YCoordinate;        

        /// <summary>
        /// ha lépett előző körben az állat és nem érte el a célkordinátát, akkor ez a cella lesz a megcélzott
        /// </summary>
        internal Cell targetCell;

        //Propertyk -->
        public int xCoordinate { get; set; }
        public int yCoordinate { get; set; }
        public string Diet { get; set; }
        public string Species { get; set; }
        public Sex SexEnumProperty { get; set; }
        public AnimalType AnimalTypeEnumProperty { get; set; }
        public int LifeSpan { get; set; }
        public int CurrentAge { get; set; }
        public int MaximumAge { get; set; }
        public int HungerLevel { get; set; }
        public int ThirstyLevel { get; set; }
        public int SurvivalRate { get; set; }

        internal abstract int RangeOfView
        {
            get;
        }

        internal abstract int RangeOfMove
        {
            get;
        }

        public int Area { get; set; }



        /*METÓDUSOK kezdete -->*/

        /// <summary>
        /// PRIVATE METHOD EAT() - ESZIK metódus
        /// </summary>
        protected abstract void Eat();

        /// <summary>
        /// ISZIK metódus
        /// </summary>
        internal abstract void Drink(ref Cell thisCell);

        /// <summary>
        /// Az állat mozgásának elindítása
        /// </summary>
        /// <param name="pAnimal">Ez az állat mozog</param>
        /// <param name="possibleCells">Ezek a cellák közül választ</param>
        internal virtual void Move(ref Cell[,] possibleCells)
        {

        }

        /// <summary>
        /// A paraméterként megadott cellák közül választ egy cellát, amire lépni fog
        /// </summary>
        /// <param name="possibleCells">Ezekből a cellákból kell választania</param>
        /// <returns></returns>
        protected virtual Cell ChooseCell(ref Cell[,] possibleCells)
        {
            throw new NotImplementedException();
        }

        protected virtual Cell ChooseCellForReproduce(Cell[,] possibleCells)
        {
            Cell maxcell = Savannah.savannah[xCoordinate, yCoordinate];
            int points;
            int maxpoints = int.MinValue;

            foreach (Cell currentCell in possibleCells)
            {
                points = 0;

                foreach (AnimalBase currentAnimal in currentCell.Animals)
                {
                    if (GetType() == currentAnimal.GetType() && SexEnumProperty != currentAnimal.SexEnumProperty && currentAnimal.CurrentAge > 4)
                    {
                        ++points;
                    }
                }

                if (points > maxpoints)
                {
                    maxcell = currentCell;
                    maxpoints = points;
                }
            }

            return maxcell;
        }

        /// <summary>
        /// Pontoz egy adott cellát
        /// </summary>
        /// <param name="pCell">A cella, amit pontoz</param>
        /// <returns></returns>
        protected virtual int CalculatePoints(Cell pCell)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// MOZOG metódus - Az állat mozgásának végrehajtása
        /// </summary>
        protected virtual void DoTheMove(ref Cell pTo, ref Cell[,] pMap)
        {

        }

        /// <summary>
        /// SZAPORODIK metódus - Új egyed(ek)et hoz létre/példányosit
        /// </summary>
        internal virtual bool Reproduce(ref Cell thisCell)
        {
            AnimalBase partner = thisCell.GetAnimalList().First(x => x.AnimalTypeEnumProperty == AnimalTypeEnumProperty && x.SexEnumProperty != SexEnumProperty);

            int number = GetReproduceChances();

            if (number>0)
            {
                return DoTheReproduce(number, partner, ref thisCell);
            }

            return false;
        }

        /// <summary>
        /// Eldönti, hogy szapoodhatnak-e az állatok és ha igen hány utódot hozhatnak világra
        /// </summary>
        /// <returns>kölykök száma</returns>
        internal virtual int GetReproduceChances()
        {
            Random rnd = new Random();

            if (rnd.Next(0, 101) > 9)
            {
                return rnd.Next(1, 5);
            }

            return rnd.Next(1,5);
        }

        /// <summary>
        /// Végrehajtja a szaporodást és a kölyköket elhelyezi a cellán
        /// </summary>
        /// <param name="numberOfPuppys">kölykök száma</param>
        /// <param name="partner">a partner(kell a típusa miatt)</param>
        /// <param name="pCell">erre a cellára fognak születni a kölykök</param>
        /// <returns></returns>
        internal virtual bool DoTheReproduce(int numberOfPuppys, AnimalBase partner, ref Cell pCell)
        {
            Type type = partner.GetType();
            ConstructorInfo constructor = type.GetConstructor(new Type[0]);

            for (int i = 0; i < numberOfPuppys; i++)
            {
                AnimalBase puppy = (AnimalBase)constructor.Invoke(new object[0]);
                puppy.HungerLevel = HungerLevel;
                pCell.AddAnimal(puppy);
            }

            return true;
        }

        /// <summary>
        /// Az állat egy körben ezzel a metódssal dönti el mit csináljon
        /// </summary>
        /// <param name="pMap">az állat látókörében lévő cellák</param>
        internal abstract void Live(ref Cell[,] pMap);

        /// <summary>
        /// Megvizsgálja, hogy az állatka tud-e szaporodni
        /// </summary>
        /// <param name="pCell">ezen a cellán keres partnert</param>
        /// <returns>van-e partner</returns>
        internal bool canReproduce(Cell pCell)
        {
            try
            {
                foreach (AnimalBase currentAnimal in pCell.GetAnimalList().Where(x => x.CurrentAge > 3))
                {
                    if (currentAnimal.animalType == this.animalType && currentAnimal.SexEnumProperty != this.SexEnumProperty)
                    {
                        return true;
                    }
                }
            }
            catch(NullReferenceException)
            {
                return true;
            }
            return false;
        }

        internal void Die(ref Cell pCell)
        {
            pCell.RemoveAnimal(this);
        }
    }
}
